﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpListenerLibrary
{
    /// <summary>
    /// 自定义Tcp服务类
    /// </summary>
    public class BL_Server
    {
        /// <summary>
        /// 服务端
        /// </summary>
        public TcpListener Server { get; set; }

        /// <summary>
        /// 客户端集合，字典形式储存
        /// </summary>
        public ConcurrentDictionary<string, TcpClient> Clients { set; get; }

        /// <summary>
        /// 配置文件的物理路径
        /// </summary>
        private string IniPath { get; }

        /// <summary>
        /// ip地址
        /// </summary>
        private string Ip { get; }

        /// <summary>
        /// 端口号
        /// </summary>
        private int Port { get; }

        /// <summary>
        /// 最大挂起连接数
        /// </summary>

        private int BackLog { get; } = int.MaxValue;

        /// <summary>
        /// 缓冲区多少K
        /// </summary>
        private int K { get; } = 10;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iniPath">配置文件的物理路径</param>
        public BL_Server(string iniPath)
        {
            IniPath = iniPath;

            string[] lines = File.ReadAllLines(IniPath, Encoding.UTF8);
            foreach (string line in lines)
            {
                // #开头的当作注释，不处理
                if (line.Trim().StartsWith("#") || line.Trim().Length == 0)
                {
                    continue;
                }

                string[] parameters = line.Split('=');
                switch (parameters[0].ToLower())
                {
                    case "ip":
                        Ip = parameters[1];
                        break;
                    case "port":
                        Port = int.Parse(parameters[1]);
                        break;
                    case "backlog":
                        BackLog = int.Parse(parameters[1]);
                        break;
                    case "k":
                        K = int.Parse(parameters[1]);
                        break;
                }
            }
        }

        /// <summary>
        /// 开启服务
        /// </summary>
        public void Start()
        {
            if (ReceiveEvent == null)
            {
                throw new Exception("接收消息事件[ReceiveEvent]未编写处理方法,开启服务失败");
            }

            IPAddress iPAddress = IPAddress.Parse(Ip);
            Server = new TcpListener(iPAddress, Port);
            Clients = new ConcurrentDictionary<string, TcpClient>();
            Server.Start(BackLog);
            Console.WriteLine($"Tcp服务端已开启,[ip={Ip},port={Port},backlog={BackLog},k={K}]");

            Task.Run(() =>
            {
                while (true)
                {
                    TcpClient client = Server.AcceptTcpClient();
                    string key = client.Client.RemoteEndPoint.ToString();
                    Clients.TryAdd(key, client);
                    Console.WriteLine($"客户端[{key}]已连接,当前连接数[{Clients.Count}]");

                    Task.Run(() =>
                    {
                        while (true)
                        {
                            try
                            {
                                byte[] buffer = new byte[1024 * K];
                                int length = client.Client.Receive(buffer);
                                //客户端主动终止连接
                                if (length == 0)
                                {
                                    try
                                    {
                                        client.Close();
                                    }
                                    catch { }
                                    Clients.TryRemove(key, out _);
                                    Console.WriteLine($"{key}客户端主动断开,当前连接数[{Clients.Count}]");
                                    break;
                                }
                                try
                                {
                                    ReceiveEvent.Invoke(key, buffer, length);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine($"[{key}]消息处理异常,请检查[ReceiveEvent事件代码]");
                                    Console.WriteLine(e.StackTrace);
                                }
                            }
                            catch (Exception e)
                            {
                                try
                                {
                                    client.Close();
                                }
                                catch { }
                                Clients.TryRemove(key, out _);
                                Console.WriteLine($"{key}客户端异常断开,当前连接数[{Clients.Count}]");
                                Console.WriteLine(e.StackTrace);
                                break;
                            }
                        }
                    });
                }
            });
        }

        /// <summary>
        /// 向所有客户端发送数据
        /// </summary>
        /// <param name="buffer">消息内容</param>
        /// 
        public void SendToAllClients(byte[] buffer)
        {
            foreach (TcpClient client in Clients.Values)
            {
                client.Client.Send(buffer);
            }
        }

        /// <summary>
        /// 向特定客户端发送数据
        /// </summary>
        /// <param name="key">客户端标识</param>
        /// <param name="buffer">消息内容</param>
        public void SendToClient(string key, byte[] buffer)
        {
            Clients[key].Client.Send(buffer);
        }

        /// <summary>
        /// 接收消息委托
        /// </summary>
        /// <param name="key"></param>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        public delegate void ReceiveDelegate(string key, byte[] buffer, int length);

        /// <summary>
        /// 接收消息事件，开启服务前必须给此事件绑定处理消息方法
        /// </summary>
        public event ReceiveDelegate ReceiveEvent;
    }
}
